#include "StdAfx.h"

#include "LinkTool.h"
#include "SegmentedWorld/SegmentedWorldManager.h"
#include "Viewport.h"
#include "Objects/EntityObject.h"

IMPLEMENT_DYNAMIC(CLinkTool,CEditTool)

//////////////////////////////////////////////////////////////////////////
CLinkTool::CLinkTool()
{
	m_pChild=NULL;
	SetStatusText("Click on object and drag a link to a new parent");

	m_hLinkCursor = AfxGetApp()->LoadCursor( IDC_POINTER_LINK );
	m_hLinkNowCursor = AfxGetApp()->LoadCursor( IDC_POINTER_LINKNOW );
	m_hCurrCursor = m_hLinkCursor;
}

//////////////////////////////////////////////////////////////////////////
CLinkTool::~CLinkTool()
{
}

//////////////////////////////////////////////////////////////////////////
void CLinkTool::LinkObject( CBaseObject * pChild, CBaseObject * pParent ) 
{
	if (pChild == NULL)
		return;

	if (ChildIsValid(pParent, pChild))
	{
		SW_TEST_OBJ_MOD(pParent, false, true);
		SW_TEST_OBJ_MOD(pChild, false, true);

		CUndo undo( "Link Object" );
		pParent->AttachChild(pChild);
		CString str;
		str.Format("%s attached to %s", pChild->GetName(), pParent->GetName());
		SetStatusText(str);
	}
	else
	{
		SetStatusText("Error: Cyclic linking or already linked.");
	}
}

//////////////////////////////////////////////////////////////////////////
void CLinkTool::LinkToBone( CEntityObject * pChild, CEntityObject * pParent, CViewport *view ) 
{
	if (pChild->GetParent() == NULL)
	{
		IEntity *pIEntity = pParent->GetIEntity();
		ICharacterInstance *pCharacter = pIEntity->GetCharacter(0);
		ISkeletonPose *pSkeletonPose = pCharacter->GetISkeletonPose();
		
		// Show a pop-up menu for selecting a bone.
		CMenu boneMenu;
		boneMenu.CreatePopupMenu();
		for (uint32 i=0; i<pSkeletonPose->GetJointCount(); ++i)
		{
			boneMenu.AppendMenu(MF_STRING, i+1, pSkeletonPose->GetJointNameByID(i));
		}

		CPoint p;
		::GetCursorPos(&p);
		unsigned int item = boneMenu.TrackPopupMenuEx( TPM_RETURNCMD|TPM_LEFTBUTTON|TPM_TOPALIGN|TPM_LEFTALIGN, p.x, p.y, view, NULL );
		if (item == 0)
			return;
		
		CUndo undo( "Link to Bone" );
		// Ok, now a bone selected.
		// Create a special entity link for the bone attachment.
		CString boneName = BONE_ATTACH_ENTITY_LINK_PREFIX;
		boneName += pSkeletonPose->GetJointNameByID(item-1);
		QuatT worldXform(pChild->GetWorldTM());
		QuatT jointWorldXform = QuatT(pParent->GetWorldTM()) * pSkeletonPose->GetAbsJointByID(item-1);
		QuatT relXform = jointWorldXform.GetInverted() * worldXform;
		pChild->AddEntityLink(boneName, pParent->GetId(),relXform.q,relXform.t);

		CString str;
		str.Format("%s attached to the bone '%s' of %s", pChild->GetName(), pSkeletonPose->GetJointNameByID(item-1), pParent->GetName());
		SetStatusText(str);
	}
	else
	{
		SetStatusText("Error: Already linked.");
	}
}

//////////////////////////////////////////////////////////////////////////
void CLinkTool::LinkSelectedToParent(CBaseObject * pParent)
{
	if (pParent)
	{
		if (IsRelevant(pParent))
		{
			CSelectionGroup * pSel = GetIEditor()->GetSelection();
			if(!pSel->GetCount())
				return;
			CUndo undo( "Link Object(s)" );
			for(int i=0; i<pSel->GetCount(); i++)
			{
				CBaseObject * pChild = pSel->GetObject(i);
				LinkObject(pChild, pParent);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CLinkTool::MouseCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	m_hCurrCursor = m_hLinkCursor;
	if (event == eMouseLDown)
	{
		HitContext hitInfo;
		view->HitTest( point,hitInfo );
		CBaseObject *obj = hitInfo.object;
		if (obj)
		{
			if (IsRelevant(obj))
			{
				m_StartDrag = obj->GetWorldPos();
				m_pChild=obj;
			}
		}
	}
	else if (event == eMouseLUp)
	{
		HitContext hitInfo;
		view->HitTest( point,hitInfo );
		CBaseObject *obj = hitInfo.object;
		if (obj)
		{
			if (IsRelevant(obj))
			{
				ISkeletonPose *pSkeletonPose = NULL;
				if (CheckVirtualKey(VK_SHIFT) && obj->IsKindOf(RUNTIME_CLASS(CEntityObject))
				&& m_pChild && m_pChild->IsKindOf(RUNTIME_CLASS(CEntityObject)))
				{
					CEntityObject *pEntity = static_cast<CEntityObject*>(obj);
					IEntity *pIEntity = pEntity->GetIEntity();
					if (pIEntity)
					{
						ICharacterInstance *pCharacter =  pIEntity->GetCharacter(0);
						if (pCharacter)
							pSkeletonPose = pCharacter->GetISkeletonPose();
					}
				}
				if (pSkeletonPose)
					LinkToBone(static_cast<CEntityObject*>(m_pChild), static_cast<CEntityObject*>(obj), view);
				else
					LinkObject(m_pChild, obj);
			}
		}
		m_pChild=NULL;
	}
	else if (event == eMouseMove)
	{
		m_EndDrag = view->ViewToWorld(point);
		
		HitContext  hitInfo;
		view->HitTest( point,hitInfo );
		CBaseObject *obj = hitInfo.object;
		if (obj)
		{
			if (IsRelevant(obj))
			{
				// Set Cursors.
				view->SetCurrentCursor( STD_CURSOR_HIT,obj->GetName() );
				if (m_pChild)
				{
					if (ChildIsValid(obj, m_pChild))
					{
						m_hCurrCursor = m_hLinkNowCursor;
					}
				}
			}
		}
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CLinkTool::OnKeyDown( CViewport *view,uint32 nChar,uint32 nRepCnt,uint32 nFlags )
{ 
	if (nChar == VK_ESCAPE)
	{
		// Cancel selection.
		GetIEditor()->SetEditTool(0);
	}
	return false; 
}

//////////////////////////////////////////////////////////////////////////
void CLinkTool::Display( DisplayContext &dc )
{
	if (m_pChild)
		dc.DrawLine(m_StartDrag, m_EndDrag, ColorF(1, 0, 0), ColorF(1, 1, 1));
}

//////////////////////////////////////////////////////////////////////////
bool CLinkTool::ChildIsValid(CBaseObject *pParent, CBaseObject *pChild, int nDir)
{
	if (!pParent)
		return false;
	if (!pChild)
		return false;
	if (pParent==pChild)
		return false;
	CBaseObject *pObj;
	if (nDir & 1)
	{
		if (pObj=pChild->GetParent())
		{
			if (!ChildIsValid(pParent, pObj, 1))
			{
				return false;
			}
		}
	}
	if (nDir & 2)
	{
		for (int i=0;i<pChild->GetChildCount();i++)
		{
			if (pObj=pChild->GetChild(i))
			{
				if (!ChildIsValid(pParent, pObj, 2))
				{
					return false;
				}
			}
		}
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CLinkTool::OnSetCursor( CViewport *vp )
{
	SetCursor( m_hCurrCursor );
	return true;
}